#include "GraphWidget.h"
#include "ui_GraphWidget.h"

#include <QPainter>
#include <QMouseEvent>
#include <QWheelEvent>
#include <QKeyEvent>
#include <QVector2D>
#include <QDebug>
#include <QFileDialog>
#include <QTime>
#include <QLabel>

#include <limits>

#include "math.h"

#define DEG2RAD(x) x * M_PI / 180.0

GraphWidget::GraphWidget(QWidget *parent) :
    QGLWidget(parent),
    ui(new Ui::GraphWidget),
    windowRect(QRectF(-10.0f, -10.0f, 20.0f, 20.0f)),
    xIntervalCount(5.0f),
    yIntervalCount(15.0f),
    xTickMarkLengthRatio(0.01f),
    yTickMarkLengthRatio(0.01f),
    axesPen(QPen(QBrush(Qt::white), 1.0f)),
    backgroundColor(Qt::black),
    selectedObjectIndex(NO_LINE),
    selectedObjectPen(QPen(QBrush(QColor(0, 50, 255, 255)), 10.0f)),
    shouldDrawTraceCoords(false),
    graphTitleColor(Qt::white),
    shouldDrawLegend(true)
{
    ui->setupUi(this);
    connect(&timer, SIGNAL(timeout()), this, SLOT(graphNextIntervalObjectList()));
}

GraphWidget::~GraphWidget()
{
    delete ui;
    foreach (GraphWidgetObject* object, objects) {
        delete object;
    }

    foreach (QVector<GraphWidgetObject*> objectList, intervalObjectLists) {
        foreach (GraphWidgetObject* object, objectList) {
            delete object;
        }
    }
}


void GraphWidget::saveToImage()
{
    QString filename = QFileDialog::getSaveFileName();

    if (filename.isEmpty())
        return;

    QImage image = this->toImage();

    image.save(filename);
}

QImage GraphWidget::toImage()
{
    return this->grabFrameBuffer(true);
}

void GraphWidget::setTitle(QString title)
{
    graphTitle = title;
    this->updateGL();
}

void GraphWidget::setTitleColor(QColor color)
{
    graphTitleColor = color;
    this->updateGL();
}

void GraphWidget::autoResizeWindowRect()
{
    float minX = std::numeric_limits<float>::max();
    float minY = std::numeric_limits<float>::max();
    float maxX = std::numeric_limits<float>::min();
    float maxY = std::numeric_limits<float>::min();

    for (int i = 0; i < objects.count(); i++) {
        GraphWidgetObject* object = objects[i];
        if (object->maxX() > maxX) {
            maxX = object->maxX();
        }
        if (object->minX() < minX) {
            minX = object->minX();
        }
        if (object->maxY() > maxY) {
            maxY = object->maxY();
        }
        if (object->minY() < minY) {
            minY = object->minY();
        }
    }

    this->setWindowRect(minX, maxX, minY, maxY);

    float xMargin = 0.15f * windowRect.width();
    float yMargin = 0.15f * windowRect.height();

    this->setWindowRect(minX - xMargin, maxX + xMargin, minY - yMargin, maxY + yMargin);
}

void GraphWidget::reset()
{
    objects.clear();
    this->updateGL();
}

void GraphWidget::setWindowRect(float xMin, float xMax, float yMin, float yMax)
{
    windowRect = QRectF(xMin, yMin, xMax - xMin, yMax - yMin);
    this->updateGL();
}

void GraphWidget::setWindowRect(QRectF rect)
{
    windowRect = rect;
    this->updateGL();
}

float GraphWidget::getXInterval()
{
    return xIntervalCount;
}

float GraphWidget::getXTickMarkLength()
{
    return xTickMarkLengthRatio;
}

float GraphWidget::getYInterval()
{
    return yIntervalCount;
}

float GraphWidget::getYTickMarkLength()
{
    return yTickMarkLengthRatio;
}

float GraphWidget::getAxesWidth()
{
    return axesPen.widthF();
}

float GraphWidget::getSelectedObjectWidth()
{
    return selectedObjectPen.widthF();
}

QColor GraphWidget::getAxesColor()
{
    return axesPen.color();
}

QColor GraphWidget::getBackgroundColor()
{
    return backgroundColor;
}

QColor GraphWidget::getSelectedObjectColor()
{
    return selectedObjectPen.color();
}

void GraphWidget::setIntervalCounts(float x, float y)
{
    xIntervalCount = x;
    yIntervalCount = y;
    this->updateGL();
}

void GraphWidget::setTickMarkLengthRatios(float x, float y)
{
    xTickMarkLengthRatio = x;
    yTickMarkLengthRatio = y;
    this->updateGL();
}

void GraphWidget::setXIntervalCount(float interval)
{
    xIntervalCount = interval;
    this->updateGL();
}

void GraphWidget::setXTickMarkLengthRatio(float length)
{
    xTickMarkLengthRatio = length;
    this->updateGL();
}

void GraphWidget::setYIntervalCount(float interval)
{
    yIntervalCount = interval;
    this->updateGL();
}

void GraphWidget::setYTickMarkLengthRatio(float length)
{
    yTickMarkLengthRatio = length;
    this->updateGL();
}

void GraphWidget::setAxesWidth(float width)
{
    axesPen.setWidthF(width);
    this->updateGL();
}

void GraphWidget::setSelectedObjectWidth(float width)
{
    selectedObjectPen.setWidthF(width);
    this->updateGL();
}


void GraphWidget::setAxesColor(QColor color)
{
    axesPen.setColor(color);
    this->updateGL();
}

void GraphWidget::setBackgroundColor(QColor color)
{
    backgroundColor = color;
    this->updateGL();
}

void GraphWidget::setSelectedObjectColor(QColor color)
{
    selectedObjectPen.setColor(color);
    this->updateGL();
}

void GraphWidget::graphHistogram(QVector<int> histogramValues, bool graphInYDirection, double start, double interval, QColor color, float width, QString title)
{
    this->graphObject(new GraphWidgetHistogram(histogramValues, graphInYDirection, start, interval, color, width, title));
}

void GraphWidget::graphHistogram(QVector<float> histogramValues, bool graphInYDirection, double start, double interval, QColor color, float width, QString title)
{
    this->graphObject(new GraphWidgetHistogram(histogramValues, graphInYDirection, start, interval, color, width, title));
}

void GraphWidget::graphHistogram(QVector<double> histogramValues, bool graphInYDirection, double start, double interval, QColor color, float width, QString title)
{
    this->graphObject(new GraphWidgetHistogram(histogramValues, graphInYDirection, start, interval, color, width, title));
}

void GraphWidget::graphHistogram(QVector<QPointF> histogramValues, QColor color, float width, QString title)
{
    this->graphObject(new GraphWidgetHistogram(histogramValues, color, width, title));
}

void GraphWidget::graphHistogram(GraphWidgetHistogram histogram)
{
    this->graphObject(new GraphWidgetHistogram(histogram));
}

void GraphWidget::graphLine(QVector<int> yValues, double xStart, double xInterval, QColor color, float width, QString title)
{
    this->graphObject(new GraphWidgetLine(yValues, xStart, xInterval, color, width, title));
}

void GraphWidget::graphLine(QVector<float> yValues, double xStart, double xInterval, QColor color, float width, QString title)
{
    this->graphObject(new GraphWidgetLine(yValues, xStart, xInterval, color, width, title));
}

void GraphWidget::graphLine(QVector<double> yValues, double xStart, double xInterval, QColor color, float width, QString title)
{
    this->graphObject(new GraphWidgetLine(yValues, xStart, xInterval, color, width, title));
}

void GraphWidget::graphLine(QVector<QPointF> values, QColor color, float width, QString title)
{
    this->graphObject(new GraphWidgetLine(values, color, width, title));
}

void GraphWidget::graphLine(QLine newLine, QColor color, float width, QString title)
{
    this->graphObject(new GraphWidgetLine(newLine, color, width, title));
}

void GraphWidget::graphLine(QLineF newLine, QColor color, float width, QString title)
{
    this->graphObject(new GraphWidgetLine(newLine, color, width, title));
}

void GraphWidget::graphLine(GraphWidgetLine line)
{
    this->graphObject(new GraphWidgetLine(line));
}

void GraphWidget::graphObject(GraphWidgetObject* object)
{
    objects.append(object);
    this->updateGL();
}


void GraphWidget::graphMultipleObjectsAtIntervals(QVector<QVector<GraphWidgetObject*> > &objectLists, int milliseconds)
{
    intervalObjectLists = objectLists;
    timer.setInterval(milliseconds);
    timer.start();
}

void GraphWidget::graphObjectsAtIntervals(QVector<GraphWidgetObject*> &objectList, int milliseconds)
{
    QVector<QVector<GraphWidgetObject*> > multipleObjects;
    foreach (GraphWidgetObject* object, objectList) {
        QVector<GraphWidgetObject*> newObjectList;
        newObjectList.append(object);
        multipleObjects.append(newObjectList);
    }

    this->graphMultipleObjectsAtIntervals(multipleObjects, milliseconds);
}

void GraphWidget::graphNextIntervalObjectList()
{
    static int i = 0;
    if (i >= intervalObjectLists.count()) {
        i = 0;
        timer.stop();
        return;
    }

    this->reset();

    for (int j = 0; j < intervalObjectLists.count(); j++) {
        QVector<GraphWidgetObject*> objectList = intervalObjectLists[i];
        for (int k = 0; k < objectList.count(); k++) {
            this->graphObject(objectList[k]);
        }
    }

    this->autoResizeWindowRect();

    i++;
}

int GraphWidget::getSelectedObjectIndex()
{
    return selectedObjectIndex;
}

void GraphWidget::setSelectedObjectIndex(int index)
{
    selectedObjectIndex = index;
    this->updateGL();
}

int GraphWidget::findLineIndexClosestToPoint(QPointF point)
{
    float yScale = windowRect.width()/windowRect.height();
//    float xScale = windowRect.height()/windowRect.width();
    point.ry() *= yScale;
//    point.rx() *= xScale;
    qDebug() << point;
    QVector2D center(point.x(), point.y());
    double minDistance = std::numeric_limits<double>::max();
    double minIndex = 0;
    float stride = 1.0f;
    if (objects.count() > 100000) {
        stride = objects.count() / 10;
    }



    for (int i = 0; i < objects.count(); i++) {
        QVector<QPointF> points = objects[i]->values();
        for (int j = 0; j < points.count(); j += stride) {
            QPointF point = points[j];
            QVector2D eachPoint(point.x(), point.y());
            if (!windowRect.contains(point))
                continue;
            QVector2D difference = center - eachPoint;
            if (difference.length() < minDistance) {
                minDistance = difference.length();
                minIndex = i;
            }
        }
    }

    return minIndex;
}

void GraphWidget::toggleObjectSelect(int index)
{
    if (index == selectedObjectIndex) {
        selectedObjectIndex = NO_LINE;
    } else {
        selectedObjectIndex = index;
    }
    this->updateGL();
}

void GraphWidget::zoomAtPoint(QPointF point, float scaleFactor)
{

    QSizeF currentSize = windowRect.size();
    QSizeF newSize = currentSize * 1.0f/scaleFactor;

    float s = fabs(point.x() - windowRect.left()) / currentSize.width();
    float t = fabs(point.y() - windowRect.top()) / currentSize.height();

    QRectF newRect = windowRect;
    newRect.setLeft(point.x() - newSize.width() * s);
    newRect.setRight(newRect.left() + newSize.width());
    newRect.setTop(point.y() - newSize.height() * t);
    newRect.setBottom(newRect.top() + newSize.height());

    this->setWindowRect(newRect);
}

void GraphWidget::translateAmount(float x, float y)
{
    QRectF newRect = windowRect;
    newRect.setLeft(windowRect.left() + x);
    newRect.setRight(windowRect.right() + x);
    newRect.setTop(windowRect.top() + y);
    newRect.setBottom(windowRect.bottom() + y);
    this->setWindowRect(newRect);
}

QPointF GraphWidget::translatePoint(QPointF point)
{
    QPointF newPoint;

    float s;
    float t;

    s = point.x() / this->width();
    t = 1.0f - point.y() / this->height();
    newPoint.setX(windowRect.width() * s + windowRect.left());
    newPoint.setY(windowRect.height() * t + windowRect.top());
    return newPoint;
}

QLineF GraphWidget::getXAxis()
{

    QPointF xAxisA(windowRect.left(), 0.0f);
    QPointF xAxisB(windowRect.right(), 0.0f);

    return QLineF(xAxisA, xAxisB);
}

QLineF GraphWidget::getYAxis()
{
    QPointF yAxisA(0.0f, windowRect.top());
    QPointF yAxisB(0.0f, windowRect.bottom());

    return QLineF(yAxisA, yAxisB);
}

void GraphWidget::drawLine(QPointF* line, int pointCount, QPen pen)
{
    glColor4f(pen.color().redF(), pen.color().greenF(), pen.color().blueF(), pen.color().alphaF());
    glLineWidth(pen.widthF());
    glEnableClientState(GL_VERTEX_ARRAY);
    glVertexPointer(2, GL_DOUBLE, 0, line);
    glDrawArrays(GL_LINE_STRIP, 0, pointCount);
    glDisableClientState(GL_VERTEX_ARRAY);

}

void GraphWidget::drawLine(QVector<QPointF>& line, QPen pen)
{
    this->drawLine(line.data(), line.count(), pen);
}

void GraphWidget::drawLine(float x1, float y1, float x2, float y2, QPen pen)
{
    glColor4f(pen.color().redF(), pen.color().greenF(), pen.color().blueF(), pen.color().alphaF());
    glLineWidth(pen.widthF());
    glBegin(GL_LINES);
    {
        glVertex2f(x1, y1);
        glVertex2f(x2, y2);
    }
    glEnd();
    glPointSize(pen.widthF() * 0.8f);
    glBegin(GL_POINTS);
    {
        glVertex2f(x1, y1);
        glVertex2f(x2, y2);
    }
    glEnd();
}

void GraphWidget::drawLine(QPointF point1, QPointF point2, QPen pen)
{
    this->drawLine(point1.x(), point1.y(), point2.x(), point2.y(), pen);
}

void GraphWidget::drawLine(QLineF line, QPen pen)
{
    this->drawLine(line.x1(), line.y1(), line.x2(), line.y2(), pen);
}

void GraphWidget::drawAxesCoord(float coord, float x, float y, bool isX)
{
    float fontSize = 8;
    float widthScale = windowRect.width()/this->width();
    float heightScale = windowRect.height()/this->height();
    QString number;
    number.sprintf("%.3f", coord);

    if (isX) {
        x -= widthScale * 2.0f * number.length();
        y -= heightScale * 15.0f;

    } else {
        x -= widthScale * 5.0f * number.length();
        y -= heightScale * 5.0f;
    }

    if (coord != 0)
        this->drawText(number, x, y, true, fontSize, axesPen.color());
}

void GraphWidget::drawAxes()
{

    float xInterval = windowRect.width()/xIntervalCount;
    float yInterval = windowRect.height()/yIntervalCount;
    float yTickMarkLength = windowRect.width() * yTickMarkLengthRatio;
    float xTickMarkLength = windowRect.height() * xTickMarkLengthRatio;
    QLineF xAxis = this->getXAxis();
    QLineF yAxis = this->getYAxis();
    this->drawLine(xAxis, axesPen);
    this->drawLine(yAxis, axesPen);

    for (float i = 0; i >= windowRect.top(); i-=yInterval) {
        QPointF left = QPointF(-yTickMarkLength/2, i);
        QPointF right = QPointF(yTickMarkLength/2, i);

        this->drawAxesCoord(i, -yTickMarkLength/2, i, false);
        this->drawLine(left, right, axesPen);
    }

    for (float i = 0; i <= windowRect.bottom(); i+=yInterval) {
        QPointF left = QPointF(-yTickMarkLength/2, i);
        QPointF right = QPointF(yTickMarkLength/2, i);

        this->drawAxesCoord(i, -yTickMarkLength/2, i, false);
        this->drawLine(left, right, axesPen);
    }

    for (float i = 0; i <= windowRect.right(); i+=xInterval) {
        QPointF top = QPointF(i, -xTickMarkLength/2);
        QPointF bottom = QPointF(i, xTickMarkLength/2);

        this->drawAxesCoord(i, i, -xTickMarkLength/2, true);
        this->drawLine(top, bottom, axesPen);
    }

    for (float i = 0; i >= windowRect.left(); i-=xInterval) {
        QPointF top = QPointF(i, -xTickMarkLength/2);
        QPointF bottom = QPointF(i, xTickMarkLength/2);

        this->drawAxesCoord(i, i, -xTickMarkLength/2, true);
        this->drawLine(top, bottom, axesPen);
    }
}

void GraphWidget::highlightSelectedLine()
{
    if (selectedObjectIndex < objects.count() && selectedObjectIndex != NO_LINE) {
        objects[selectedObjectIndex]->select(selectedObjectPen);
    }
}

void GraphWidget::drawTracePoint()
{
    if (selectedObjectIndex != NO_LINE) {
        QPen tracePen(Qt::red);
        tracePen.setWidthF(0.75f);
        glColor4f(tracePen.color().redF(), tracePen.color().greenF(), tracePen.color().blueF(), tracePen.color().alphaF());
        glLineWidth(tracePen.width());
        float widthFactor = windowRect.width()/this->width();
        float heightFactor = windowRect.height()/this->height();

        float radius = 10.0f;
        glBegin(GL_LINE_LOOP);
        {
            for (int i = 0; i < 360; i+=3) {
                double rads = DEG2RAD(i);
                glVertex2f(radius * widthFactor * cos(rads) + tracePoint.x(), radius * heightFactor * sin(rads) + tracePoint.y());
            }
        }
        glEnd();

        this->drawLine(widthFactor * radius + tracePoint.x(), tracePoint.y(),
                       widthFactor * -radius + tracePoint.x(), tracePoint.y(), tracePen);

        this->drawLine(tracePoint.x(), heightFactor * radius + tracePoint.y(),
                       tracePoint.x(), heightFactor * -radius + tracePoint.y(), tracePen);
    }

}

void GraphWidget::drawObjects()
{
    this->highlightSelectedLine();

    for (int i = 0; i < objects.count(); i++) {
        objects[i]->draw();
    }

    this->drawTracePoint();
}

void GraphWidget::drawTitle()
{
    if (graphTitle.isEmpty())
        return;
    this->drawText(graphTitle,
                   this->width()/3.33f + graphTitle.length()/2.0f,
                   0.0f, false, 10.0f, graphTitleColor, true, backgroundColor);
}

void GraphWidget::drawText(QString text, float x, float y, bool isWorldCoordinates, float size, QColor color,
                           bool shouldHighlight, QColor highlightColor)
{

    float heightScale = windowRect.height()/this->height();
    float widthScale = windowRect.width()/this->width();

    QLabel label (text, this);
    label.setFont(QFont("Arial", size));
    label.adjustSize();

    QSizeF worldSize(label.size());
    worldSize.rwidth() *= widthScale;
    worldSize.rheight() *= heightScale;

    float boxLeft = x;
    float boxRight = boxLeft + worldSize.width();

    float boxTop = y;
    float boxBottom = boxTop + worldSize.height();

    if (!isWorldCoordinates) {
        y = -y;
        y -= size + 1;
        x = x * widthScale + windowRect.left();
        y = y * heightScale + windowRect.bottom();
    }

    if (shouldHighlight) {
        this->drawRect(boxLeft, boxTop, boxRight, boxBottom, isWorldCoordinates, highlightColor, true);
    }
    glColor4f(color.redF(), color.greenF(), color.blueF(), color.alphaF());
    this->renderText(x, y, 0.0f, text, QFont("Arial", size));
}

void GraphWidget::drawRect(float left, float top, float right, float bottom,
                           bool isWorldCoordinates, QColor color, bool shouldKeepOriginalSize)
{
    float heightScale = windowRect.height()/this->height();
    float widthScale = windowRect.width()/this->width();

    if (!isWorldCoordinates) {
        float width = right - left;
        float height = top - bottom;
        top = -top * heightScale + windowRect.bottom();
        left = left * widthScale + windowRect.left();
        if (shouldKeepOriginalSize) {
            right = left + width;
            bottom = top + height;
        } else {
            right = left + width * widthScale;
            bottom = top + height * heightScale;
        }
    }


    glColor4f(color.redF(), color.greenF(), color.blueF(), color.alphaF());
    glBegin(GL_QUADS);
    {
        glVertex2f(left, top);
        glVertex2f(right, top);
        glVertex2f(right, bottom);
        glVertex2f(left, bottom);
    }
    glEnd();
}

void GraphWidget::drawTraceCoords()
{
    if (selectedObjectIndex != NO_LINE && shouldDrawTraceCoords) {

        float fontSize = 10.0f;
        QString number;
        QString xCoord = "X: " + number.sprintf("%.8f", tracePoint.x());
        QString yCoord = "Y: " + number.sprintf("%.8f", tracePoint.y());
        int bigLength = xCoord.length();
        if (yCoord.length() > bigLength)
            bigLength = yCoord.length();

        float x = this->width() - bigLength * fontSize * 0.75f;
        float y = this->height() - fontSize * 3.5f;
        this->drawText(xCoord,
                       x,
                       y,
                       false,
                       fontSize,
                       axesPen.color(),
                       true,
                       backgroundColor);
        y = this->height() - fontSize * 1.5f;
        this->drawText(yCoord,
                       x,
                       y,
                       false,
                       fontSize,
                       axesPen.color(),
                       true,
                       backgroundColor);
    }
}

void GraphWidget::drawLegend()
{

    if (!shouldDrawLegend)
        return;
    float keySpacing = 15.0f;
    float legendHeight = keySpacing * (objects.count() + 1);
    QColor color = backgroundColor;
    color.setAlphaF(0.75f);

    int maxTitleWidth = std::numeric_limits<int>::min();
    QLabel label;
    label.setFont(QFont("Arial", 10));
    foreach (GraphWidgetObject* object, objects) {
        label.setText(object->title());
        label.adjustSize();
        if (label.width() > maxTitleWidth) {
            maxTitleWidth = label.width();
        }
    }

    label.setFont(QFont("Arial", 12));
    label.setText("Legend");
    label.adjustSize();
    if (label.width() > maxTitleWidth)
        maxTitleWidth = label.width();

    float xOffset = maxTitleWidth;
    float left = this->width() - xOffset;
    this->drawRect(left - 25, 0, this->width(), legendHeight, false, color);
    this->drawText(label.text(), left, 0, false, 12, axesPen.color());

    float i = keySpacing;
    foreach (GraphWidgetObject* object, objects) {
        this->drawRect(left - 20, i, left - 10, i + 10, false, object->color());
        this->drawText(object->title(), left - 5, i, false, 10, object->color());
        i+= keySpacing;
    }
}

void GraphWidget::resizeGL(int w, int h)
{
    glViewport(0, 0, w, h);
}

void GraphWidget::initializeGL()
{
    glEnable(GL_BLEND);
    glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
    glEnable(GL_LINE_SMOOTH);
    glHint(GL_LINE_SMOOTH_HINT, GL_NICEST);
    glEnable(GL_POINT_SMOOTH);
    glHint(GL_POINT_SMOOTH_HINT, GL_NICEST);
    glDisable(GL_DEPTH_TEST);
    glLoadIdentity();
    glOrtho(windowRect.left(), windowRect.right(), windowRect.top(), windowRect.bottom(), -10.0f, 10.0f);
}

void GraphWidget::paintGL()
{
    glLoadIdentity();
    glOrtho(windowRect.left(), windowRect.right(), windowRect.top(), windowRect.bottom(), -10.0f, 10.0f);
    glClearColor(backgroundColor.redF(), backgroundColor.greenF(), backgroundColor.blueF(), backgroundColor.alphaF());
    glClear(GL_DEPTH_BUFFER_BIT | GL_COLOR_BUFFER_BIT);

    this->drawAxes();
    this->drawObjects();
    this->drawTitle();
    this->drawTraceCoords();
    this->drawLegend();

}

void GraphWidget::mousePressEvent(QMouseEvent* event)
{
    if (event->buttons() & Qt::LeftButton) {
        mouseStartingPoint = event->posF();
        mouseMovingPoint = event->posF();
    }
}

void GraphWidget::mouseMoveEvent(QMouseEvent* event)
{
    if (event->buttons() & Qt::LeftButton) {
        float x = (mouseMovingPoint.x() - event->posF().x()) * windowRect.width()/this->width();
        float y = -1 * (mouseMovingPoint.y() - event->posF().y()) * windowRect.height()/this->height();
        mouseMovingPoint = event->posF();
        this->translateAmount(x, y);

    } else if (event->buttons() & Qt::RightButton) {
        if (selectedObjectIndex != NO_LINE) {
            shouldDrawTraceCoords = true;
        }

        QPointF point = this->translatePoint(event->posF());

        tracePoint.setX(point.x());

        this->traceValueEntered();
    }

}

void GraphWidget::mouseReleaseEvent(QMouseEvent* event)
{
    if (event->button() == Qt::LeftButton) {

        if (fabs(mouseStartingPoint.x() - event->x()) > 0.001 || fabs(mouseStartingPoint.y() - event->y()) > 0.001) {
            return;
        }
        QPointF graphPoint = this->translatePoint(event->posF());
        this->toggleObjectSelect(this->findLineIndexClosestToPoint(graphPoint));
    } else if (event->button() == Qt::RightButton) {
        if (selectedObjectIndex != NO_LINE) {
            shouldDrawTraceCoords = true;
        }
        QPointF point = this->translatePoint(event->posF());

        tracePoint.setX(point.x());

        this->traceValueEntered();
    }
}

void GraphWidget::keyPressEvent(QKeyEvent* event)
{
    if (event->key() == 'T') {
        shouldDrawTraceCoords = !shouldDrawTraceCoords;
        this->updateGL();
    } else if (event->key() == 'S') {
        this->saveToImage();
    } else if (event->key() == 'L') {
        shouldDrawLegend = !shouldDrawLegend;
        this->updateGL();
    }
}

void GraphWidget::wheelEvent(QWheelEvent* event)
{
    float steps = event->delta()/2500.0f;

    float zoom = 1 + steps;

    QPointF point(event->x(), event->y());

    this->zoomAtPoint(this->translatePoint(point), zoom);
}

void GraphWidget::traceValueEntered()
{
    if (selectedObjectIndex == NO_LINE) {
        return;
    }

    float xValue = tracePoint.x();

    float yValue = objects[selectedObjectIndex]->yValueAt(xValue);

    tracePoint.setX(xValue);
    tracePoint.setY(yValue);

    this->updateGL();

}
